<template>
  <div class="asset-tree-table" :style="rootStyle">
    <!-- 顶部：搜索区（可配） -->
    <el-form
      :model="queryParams"
      ref="queryRef"
      :inline="true"
      v-show="showSearch"
      label-width="68px"
      class="mb12"
    >
      <el-form-item label="设备名称" prop="name">
        <el-input
          v-model="queryParams.name"
          placeholder="请输入设备名称"
          clearable
          @keyup.enter="handleQuery"
          @clear="handleQuery"
          class="inputWidth"
           
        />
      </el-form-item>
      <el-form-item label="设备编码" prop="code">
        <el-input
          v-model="queryParams.code"
          placeholder="请输入设备编码"
          clearable
          @keyup.enter="handleQuery"
          @clear="handleQuery"
          class="inputWidth"
        />
      </el-form-item>
      <el-form-item label="创建时间" prop="createTime">
        <el-date-picker
          clearable
          v-model="queryParams.createTime"
          type="date"
          value-format="YYYY-MM-DD"
          placeholder="请选择创建时间"
          @clear="handleQuery"
          @change="handleQuery"
        />
      </el-form-item>
      <el-form-item label="更新时间" prop="updateTime">
        <el-date-picker
          clearable
          v-model="queryParams.updateTime"
          type="date"
          value-format="YYYY-MM-DD"
          placeholder="请选择更新时间"
          @clear="handleQuery"
          @change="handleQuery"
        />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button>
        <el-button :icon="Refresh" @click="resetQuery">重置</el-button>
      </el-form-item>
    </el-form>

    <el-row :gutter="12" class="main-row">
      <!-- 左：树 -->
      <el-col :span="leftSpan">
        <el-card shadow="never" class="left-card" body-style="padding:0;">
          <div class="left-body">
            <div class="left-top"></div>
            <div class="left-middle">
              <el-tree
                ref="treeRef"
                class="asset-tree"
                :data="treeDataPaged"
                node-key="id"
                :props="treeProps"
                :expand-on-click-node="false"
                :highlight-current="true"
                :filter-node-method="filterNode"
                :check-strictly="true"
                :default-checked-keys="selectedIds"
                @check="onTreeCheck"
                @node-click="onNodeClick"
              >
                <template #default="{ data, node }">
                  <div
                    class="node-line"
                    :style="{ '--lvl-color': levelColor(data.level) }"
                    @dblclick.stop="onNodeDblclick(data)"
                  >
                    <div class="node-left">
                      <el-icon v-if="data.type === '系统'"><Cpu /></el-icon>
                      <el-icon v-else-if="data.type === '母设备'"><Setting /></el-icon>
                      <el-icon v-else-if="data.type === '子设备'"><Cpu /></el-icon>
                      <el-icon v-else-if="data.type === '部件'"><Tools /></el-icon>
                      <el-icon v-else><Cpu /></el-icon>
                      <span class="font-500 name-colored">{{ data.name }}</span>
                      <div
                        class="root-tools"
                        v-if="isRoot(data) && data.children?.length > 0"
                      >
                        <span
                          class="root-arrow"
                          :data-state="node.expanded ? 'open' : 'close'"
                          :title="node.expanded ? '收起' : '展开'"
                          @click.stop="onRootToggle(node, data)"
                        >
                          {{ node.expanded ? "↑" : "↓" }}
                        </span>
                      </div>
                    </div>
                    <div class="node-right">
                      <el-tag
                        v-if="data.children?.length"
                        size="small"
                        title="子数量"
                      >
                        {{ data.children.length }}
                      </el-tag>
                      <el-tag
                        size="small"
                        class="tag-soft"
                        :style="softTagStyle(data.type)"
                      >
                        {{ data.type.length === 3 ? data.type.slice(0, 2) : data.type }}
                      </el-tag>
                    </div>
                  </div>
                </template>
              </el-tree>
            </div>
            <div class="left-bottom">
              <el-divider class="m-0" />
              <div class="flex items-center justify-between p-12">
                <el-pagination
                  :background="true"
                  v-model:current-page="currentPage"
                  v-model:page-size="pageSize"
                  :layout="layout"
                  :page-sizes="pageSizes"
                  :pager-count="pagerCount"
                  :total="rootTotal"
                  @size-change="handleSizeChange"
                  @current-change="onPageChange"
                />
                <el-button :icon="allExpanded ? Fold : Expand" @click="toggleExpandAll" text>
                  {{ allExpanded ? "收起全部" : "展开全部" }}
                </el-button>
              </div>
            </div>
          </div>
        </el-card>
      </el-col>

      <!-- 右：表 -->
      <el-col :span="24 - leftSpan">
        <el-card shadow="never">
          <template #header>
            <div class="flex items-center justify-between" :style="{ marginLeft: '2px', marginRight: '7px' }">
              <div class="flex items-center gap-8">
                <el-breadcrumb separator="/">
                  <el-breadcrumb-item v-for="p in breadcrumb" :key="p.id">{{ p.name }}</el-breadcrumb-item>
                </el-breadcrumb>
              </div>
              <div class="flex items-center gap-8">
                <el-tag v-if="currentNode" type="success">当前：{{ currentNode?.name }}</el-tag>
                <el-tag v-if="filteredRows.length" type="warning">显示：{{ filteredRows.length }} 项</el-tag>
              </div>
            </div>
          </template>
          

          <el-table
  :key="tableKey"
  ref="tableRef"
  :data="filteredRows"
  row-key="id"
  border
  :height="tableHeight"
  stripe
  :row-class-name="rowClassName"
  :tree-props="{ children: '_noChildren_' }"
  :fit="false"               
  style="width: 100%"
  @row-dblclick="onRowDblclick"
>
  <!-- 360 -->
  <el-table-column label="名称 / 编码" width="360">
    <template #default="{ row }">
      <div class="name-code-cell">
        <div class="cell-left">
          <el-icon v-if="row.type === '系统'"><Cpu/></el-icon>
          <el-icon v-else-if="row.type === '母设备'"><Setting/></el-icon>
          <el-icon v-else-if="row.type === '子设备'"><Cpu/></el-icon>
          <el-icon v-else-if="row.type === '部件'"><Tools/></el-icon>
          <el-icon v-else><Cpu/></el-icon>
          <span class="name-text">{{ row.name }}</span>
        </div>
        <div class="cell-right">
          <el-tag size="small" type="info">{{ row.code }}</el-tag>
          <el-tag size="small" class="ml4" :type="tagTypeOf(row.type)">{{ row.type }}</el-tag>
        </div>
      </div>
    </template>
  </el-table-column>

  <!-- 520 -->
  <el-table-column label="层级路径" width="520" align="left" show-overflow-tooltip>
    <template #default="{ row }">{{ pathText(row) }}</template>
  </el-table-column>

  <!-- 220 -->
  <el-table-column label="备件名称" width="220" align="left">
    <template #default="{ row }">{{ row?.spareInfo?.name || "-" }}</template>
  </el-table-column>

  <!-- 120 / 120 / 100 / 120 -->
  <el-table-column label="总库存"   width="120" align="center">
    <template #default="{ row }">{{ row?.spareInfo?.stockTotal || "-" }}</template>
  </el-table-column>
  <el-table-column label="可用库存" width="120" align="center">
    <template #default="{ row }">{{ row?.spareInfo?.stockAvailable || "-" }}</template>
  </el-table-column>
  <el-table-column prop="level" label="层级" width="100" align="center" />
  <el-table-column label="子项数量" width="120" align="center">
    <template #default="{ row }">{{ row.children?.length || 0 }}</template>
  </el-table-column>

  <!-- 200 / 200 -->
  <el-table-column label="创建时间" width="200">
    <template #default="{ row }">
      {{ row.createTime ? parseTime(row.createTime, "{y}-{m}-{d} {h}:{i}:{s}") : "-" }}
    </template>
  </el-table-column>
  <el-table-column label="更新时间" width="200">
    <template #default="{ row }">
      {{ row.updateTime ? parseTime(row.updateTime, "{y}-{m}-{d} {h}:{i}:{s}") : "-" }}
    </template>
  </el-table-column>

  <!-- 260 -->
  <el-table-column prop="remark" label="备注" width="260" align="center" show-overflow-tooltip />
</el-table>

        </el-card>
      </el-col>
    </el-row>

    <!-- 详情弹窗（可选） -->
    <el-dialog v-model="detailVisible" title="节点详情" width="850px"
      :close-on-click-modal="false" :close-on-press-escape="false" :show-close="false">
      <el-descriptions :column="2" border class="desc-box">
        <el-descriptions-item label="名称">{{ detailRow?.name }}</el-descriptions-item>
        <el-descriptions-item label="编码">{{ detailRow?.code }}</el-descriptions-item>
        <el-descriptions-item label="类型">{{ detailRow?.type }}</el-descriptions-item>
        <el-descriptions-item label="层级">{{ detailRow?.level }}</el-descriptions-item>
        <el-descriptions-item label="层级路径" :span="2">
          <el-tooltip :content="pathText(detailRow)" placement="top">
            <span class="one-line">{{ pathText(detailRow) }}</span>
          </el-tooltip>
        </el-descriptions-item>
        <el-descriptions-item label="备件名称">{{ detailRow?.spareInfo?.name || "-" }}</el-descriptions-item>
        <el-descriptions-item label="备件编码">{{ detailRow?.spareInfo?.code || "-" }}</el-descriptions-item>
        <el-descriptions-item label="总库存">{{ detailRow?.spareInfo?.stockTotal || "-" }}</el-descriptions-item>
        <el-descriptions-item label="可用库存">{{ detailRow?.spareInfo?.stockAvailable || "-" }}</el-descriptions-item>
        <el-descriptions-item label="创建时间">
          {{ detailRow?.createTime ? parseTime(detailRow.createTime, "{y}-{m}-{d} {h}:{i}:{s}") : "-" }}
        </el-descriptions-item>
        <el-descriptions-item label="更新时间">
          {{ detailRow?.updateTime ? parseTime(detailRow.updateTime, "{y}-{m}-{d} {h}:{i}:{s}") : "-" }}
        </el-descriptions-item>
        <el-descriptions-item label="备注" :span="2">
          <span class="one-line">{{ detailRow?.remark || "-" }}</span>
        </el-descriptions-item>
      </el-descriptions>
      <template #footer><el-button @click="detailVisible = false">关闭</el-button></template>
    </el-dialog>
  </div>
</template>

<script setup>
/**
 * 可复用资产树+表组件
 * Props：
 *  - startParentId: number|null   初始聚焦的父ID（可为空）
 *  - width/height: string         外层容器宽/高（如 '100%', '80vh'）
 *  - leftSpan: number             左侧树的span（默认6）
 *  - tableHeight: string|number   右侧表格高度（默认 558）
 *  - showSearch: boolean          是否显示顶部搜索（默认 true）
 *  - pageSizeDefault: number      根层分页大小（默认 10）
 * Emits：
 *  - node-click(data)             点击树节点
 *  - row-dblclick(row)            双击表格行
 *  - loaded({ roots, total })     数据加载完毕
 * 暴露方法：
 *  - reload(opts?)                重新加载
 *  - reloadSearch(opts?)
 *  - focusNodeById(id)
 */

import {
  ref, reactive, toRefs, computed, onMounted, nextTick, getCurrentInstance, defineExpose
} from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import {
  ArrowDown, ArrowUp, Search, Close, Plus, CopyDocument, Expand, Fold, Setting, Tools, Cpu, Refresh
} from "@element-plus/icons-vue";

// === 你的 API 保持不变，仅在组件内引用 ===
import {
  listSbtz, listSearchData, getSbtz, delSbtz, addSbtz, updateSbtz,
  cascaderChildren, bulkCreateChain, createRoute, rehomeRoute
} from "@/api/sbtz/sbtz";
import { listSpare } from "@/api/sbtz/spare";
import { listSbtype } from "@/api/sbtz/sbtype";
import { loadAllParams } from "@/api/page";

const props = defineProps({
  startParentId: { type: [Number, String], default: null },
  width: { type: String, default: '100%' },
  height: { type: String, default: 'calc(92vh - 140px)' },
  leftSpan: { type: Number, default: 6 },
  tableHeight: { type: [String, Number], default: 558 },
  showSearch: { type: Boolean, default: true },
  pageSizeDefault: { type: Number, default: 10 },
});
const emit = defineEmits(["node-click", "row-dblclick", "loaded"]);

const rootStyle = computed(() => ({ width: props.width, height: 'auto' }));

/* ======= 你的原有状态/计算，做了小幅收口 ======= */
const pageSizes = [10, 20, 50, 100];
const pagerCount = 7;

const treeRef = ref();
const tableRef = ref();
const currentPage = ref(1);
const rootTotalRef = ref(0);
const allRoots = ref([]);
const idMap = ref(new Map());
const currentNode = ref(null);
const breadcrumb = ref([]);
const selectedIds = ref([]);

const showSearchRef = ref(props.showSearch);
const pageSize = ref(props.pageSizeDefault);

const filterText = ref("");
const tableFilter = ref("");

const treeProps = { children: "children", label: "name", disabled: "disabled" };
const treeDataPaged = computed(() => allRoots.value);
const rootTotal = computed(() => rootTotalRef.value);
const currentId = computed(() => currentNode.value?.id);
const tableKey = computed(() => `table-${currentId.value ?? 0}`);

const displayedRows = computed(() => {
  const n = currentNode.value;
  if (!n) return [];
  return [n, ...(Array.isArray(n.children) ? n.children : [])];
});
const filteredRows = computed(() => {
  const kw = (tableFilter.value || "").trim().toLowerCase();
  const rows = displayedRows.value;
  if (!kw) return rows;
  return rows.filter((r) =>
    ((r?.name ?? "") + (r?.code ?? "") + (r?.remark ?? "")).toLowerCase().includes(kw)
  );
});

function rowClassName({ row }) {
  return currentId.value && row.id === currentId.value ? "row-current" : "";
}

function tagTypeOf(t) {
  switch (t) {
    case "系统": return "success";
    case "母设备": return "primary";
    case "子设备": return "warning";
    case "部件": return "danger";
    default: return "";
  }
}

const TAG_COLORS = {
  系统: "#67C23A", SYSTEM: "#67C23A",
  母设备: "#409EFF", MACHINE: "#409EFF",
  子设备: "#E6A23C", MODULE: "#E6A23C",
  部件: "#F56C6C", COMPONENT: "#F56C6C",
  零件: "#909399", PART: "#909399",
  default: "#1ABC9C",
};
function hex2rgba(hex, a = 1) {
  const h = hex.replace("#", "");
  const r = parseInt(h.slice(0, 2), 16);
  const g = parseInt(h.slice(2, 4), 16);
  const b = parseInt(h.slice(4, 6), 16);
  return `rgba(${r},${g},${b},${a})`;
}
function getColor(type) {
  const k = String(type || "");
  return TAG_COLORS[k] || TAG_COLORS[k.toUpperCase()] || TAG_COLORS.default;
}
function softTagStyle(type) {
  const c = getColor(type);
  return {
    "--tag-fg": c,
    "--tag-bg": hex2rgba(c, 0.12),
    "--tag-bd": hex2rgba(c, 0.22),
  };
}
function levelColor(level) {
  const palette = ["#67C23A","#409EFF","#E6A23C","#F56C6C","#909399","#9B59B6","#1ABC9C","#2D8CF0"];
  return palette[(Math.max(1, level) - 1) % palette.length];
}
function isRoot(d) {
  return d?.pid == null || d?.pid === 0 || d?.level === 1;
}
function onRootToggle(node, data) {
  const open = !node.expanded;
  expandRootSubtree(data, open);
  safeExpand(node, open);
}
function safeExpand(treeNode, open = true) {
  if (!treeNode) return;
  const tree = treeRef.value;
  if (tree && typeof tree.expandNode === "function") {
    tree.expandNode(treeNode.data ?? treeNode, open);
    return;
  }
  if (open) {
    if (!treeNode.expanded) {
      treeNode.expand?.();
      treeNode.expanded = true;
    }
  } else {
    if (treeNode.expanded) {
      if (typeof treeNode.collapse === "function") treeNode.collapse();
      else treeNode.expand?.();
      treeNode.expanded = false;
    }
  }
}
function expandRootSubtree(rootData, open) {
  if (!rootData) return;
  const ids = collectIds([rootData], []);
  ids.forEach((id) => {
    const node = treeRef.value?.getNode(id);
    if (node) safeExpand(node, open);
  });
}
function collectIds(nodes, out = []) {
  for (const n of nodes || []) {
    out.push(n.id);
    if (n.children?.length) collectIds(n.children, out);
  }
  return out;
}

const allExpanded = ref(false);
async function expandAll(open) {
  await nextTick();
  const ids = collectIds(treeDataPaged.value, []);
  ids.forEach((id) => {
    const node = treeRef.value?.getNode(id);
    safeExpand(node, open);
  });
}
async function toggleExpandAll() {
  const open = !allExpanded.value;
  await expandAll(open);
  allExpanded.value = open;
}

/* ======= 数据获取与适配 ======= */
const TYPE_MAP = {
  SYSTEM: "系统", MACHINE: "母设备", MODULE: "子设备", COMPONENT: "部件", PART: "零件", SUBCOMP: "子部件",
};

let rootReqId = 0;
let childReqId = 0;
const childrenCache = new Map();

function adaptRow(r = {}) {
  const spare = r.spareInfo || {};
  const childCount =
    r.childCount ?? r.childrenCount ?? r.subCount ?? r.childrenNum ?? r.childNum ??
    (Array.isArray(r.children) ? r.children.length : 0) ?? 0;

  return {
    id: r.id,
    pid: r.parentId ?? r.pid ?? 0,
    name: r.name || "",
    code: r.code || "",
    createTime: r.createTime || "",
    updateTime: r.updateTime || "",
    type: (TYPE_MAP && TYPE_MAP[r.nodeType]) || r.type || r.nodeType || "系统",
    level: r.level ?? 1,
    remark: r.remark || "",
    childCount,
    hasChildren: childCount > 0,
    spareInfo: {
      id: spare.id ?? null,
      code: spare.code || "",
      name: spare.name || "",
      stockTotal: spare.stockTotal ?? 0,
      stockAvailable: spare.stockAvailable ?? 0,
      qtyNeed: spare.qtyNeed ?? null,
      remark: spare.remark || "",
    },
    hasRecordPart: !!r.hasRecordPart,
    recordTotal: r.recordTotal ?? 0,
    recordRemain: r.recordRemain ?? 0,
    disabled: false,
    children: [],
  };
}

async function fetchRootPage(pageNum) {
  const rid = ++rootReqId;
  try {
    const params = {
      ...queryParams.value,
      parentId: 0,
      pageNum,
      pageSize: pageSize.value,
      ...(props.startParentId ? { parentId: props.startParentId } : {}),
    };
    const resp = await listSbtz(params);
    const rows = resp?.rows || [];
    const total = Number(resp?.total || 0);
    if (rid !== rootReqId) return [];
    rootTotalRef.value = total;
    return rows.map(adaptRow);
  } catch (e) {
    rootTotalRef.value = 0;
    return [];
  }
}

async function selfSearchData(pageNum) {
  const rid = ++rootReqId;
  try {
    const params = {
      ...queryParams.value,
      pageNum,
      pageSize: pageSize.value,
    };
    const resp = await listSearchData(params);
    const rows = resp?.rows || [];
    const total = Number(resp?.total || 0);
    if (rid !== rootReqId) return [];
    rootTotalRef.value = total;
    return rows.map(adaptRow);
  } catch (e) {
    rootTotalRef.value = 0;
    return [];
  }
}

async function fetchChildren(nodeId, opts) {
  const force = opts && opts.force;
  if (!force && childrenCache.has(nodeId)) {
    return childrenCache.get(nodeId);
  }
  const rid = ++childReqId;
  try {
    const resp = await listSbtz({
      ...queryParams.value,
      parentId: nodeId,
      pageNum: 1,
      pageSize: 1000,
    });
    const rows = resp?.rows || [];
    if (rid !== childReqId) return [];
    const list = rows.map(adaptRow);
    childrenCache.set(nodeId, list);
    return list;
  } catch {
    return [];
  }
}

async function loadSubtree(node, depth = 1, maxDepth = 8, force = false) {
  const children = await fetchChildren(node.id, { force });
  node.children = children;
  if (depth >= maxDepth) return;
  for (const c of children) await loadSubtree(c, depth + 1, maxDepth, force);
}
function rebuildIndex() {
  const m = new Map();
  const st = [...allRoots.value];
  while (st.length) {
    const n = st.pop();
    m.set(n.id, n);
    (n.children || []).forEach((c) => st.push(c));
  }
  idMap.value = m;
}

/* ======= 交互 ======= */
function buildPathForBreadcrumb(treeNode) {
  const arr = [];
  let p = treeNode;
  while (p) {
    if (p.data) arr.unshift(p.data);
    p = p.parent;
  }
  return arr;
}
const onNodeClick = async (data, treeNode) => {
  currentNode.value = data;
  breadcrumb.value = buildPathForBreadcrumb(treeNode);
  emit("node-click", data);
};
function onNodeDblclick(row) {
  const node = treeRef.value?.getNode(row?.id);
  if (node) safeExpand(node, !node.expanded);
}
const onRowDblclick = async (row) => {
  await nextTick();
  const node = treeRef.value?.getNode(row.id);
  if (node) {
    let p = node.parent;
    while (p) { safeExpand(p, true); p = p.parent; }
    treeRef.value?.setCurrentKey(row.id);
    breadcrumb.value = buildPathForBreadcrumb(node);
  }
  currentNode.value = row;
  emit("row-dblclick", row);
};
function pathText(row) {
  if (!row) return "";
  const parts = [];
  let cur = row;
  while (cur) {
    parts.unshift(`${cur.name ?? ""}(${cur.code ?? ""})`);
    if (cur.pid == null) break;
    cur = idMap.value.get(cur.pid);
  }
  return parts.join(" / ");
}
function onTreeCheck(_data, info) {
  selectedIds.value = Array.isArray(info?.checkedKeys) ? info.checkedKeys : [];
}
function filterNode(value, data) {
  if (!value) return true;
  const v = value.toLowerCase();
  return (String(data.name || "") + String(data.code || "")).toLowerCase().includes(v);
}
async function handleSizeChange() {
  await reload();
}
async function onPageChange(p) {
  currentPage.value = p;
  await reload();
  await nextTick();
  treeRef.value?.setCheckedKeys(selectedIds.value, false);
}

/* ======= 搜索/重置 ======= */
const data = reactive({
  queryParams: {
    pageNum: 1,
    pageSize: 10,
    name: null,
    nodeType: null,
    code: null,
    parentId: null,
    ancestors: null,
    path: null,
    status: null,
    createTime: null,
    updateTime: null,
    remark: null,
  },
});
const { queryParams } = toRefs(data);

const isBlank = (v) =>
  v == null ||
  (typeof v === "string" && v.trim() === "") ||
  (Array.isArray(v) && v.length === 0) ||
  (typeof v === "object" && !Array.isArray(v) && Object.keys(v).length === 0);

function handleQuery() {
  queryParams.value.pageNum = 1;
  const hasValue = Object.entries(queryParams.value)
    .filter(([k]) => !["pageNum", "pageSize"].includes(k))
    .some(([, v]) => !isBlank(v));
  hasValue ? reloadSearch({ searchopt: 1 }) : reload();
}
function resetQuery() {
  queryParams.value = {
    pageNum: 1, pageSize: 10, name: null, nodeType: null, code: null,
    parentId: null, ancestors: null, path: null, status: null,
    createTime: null, updateTime: null, remark: null,
  };
  reload();
}

/* ======= 刷新 & 定位 ======= */
function walk(nodes = []) {
  const stack = Array.isArray(nodes) ? [...nodes] : [];
  const out = [];
  while (stack.length) {
    const n = stack.shift();
    if (!n) continue;
    out.push(n);
    if (Array.isArray(n.children) && n.children.length) {
      stack.unshift(...n.children);
    }
  }
  return out;
}
function getLastLeafId() {
  for (const [_, node] of [...idMap.value].reverse()) {
    if (!node.children || node.children.length === 0) return node.id;
  }
  return null;
}
function findBestMatchIdByName(name) {
  const kw = String(name || "").trim().toLowerCase();
  if (!kw) return null;
  const nodes = walk(allRoots.value);
  const exact = [], starts = [], includes = [];
  nodes.forEach((n) => {
    const nm = String(n.name || "").toLowerCase();
    if (!nm) return;
    if (nm === kw) exact.push(n);
    else if (nm.startsWith(kw)) starts.push(n);
    else if (nm.includes(kw)) includes.push(n);
  });
  const pick = (arr) => {
    if (!arr || !arr.length) return null;
    arr.sort((a, b) => {
      const leafA = a.children && a.children.length ? 0 : 1;
      const leafB = b.children && b.children.length ? 0 : 1;
      if (leafA !== leafB) return leafB - leafA;
      return (Number(b.level || 0) - Number(a.level || 0));
    });
    return arr[0];
  };
  const hit = pick(exact) || pick(starts) || pick(includes);
  return hit ? hit.id : null;
}

async function focusNodeById(targetId) {
  await nextTick();
  const node = treeRef.value?.getNode(targetId);
  if (!node) return false;
  let p = node.parent;
  while (p) { safeExpand(p, true); p = p.parent; }
  treeRef.value?.setCurrentKey(targetId);
  breadcrumb.value = buildPathForBreadcrumb(node);
  currentNode.value = node.data;
  await nextTick();
  const el = document.querySelector(".el-tree-node.is-current");
  el?.scrollIntoView({ block: "center", behavior: "smooth" });
  return true;
}

function resetChildrenCache() { childrenCache.clear(); }

async function reload(opts = { focusId: null, force: false }) {
  if (opts?.force) resetChildrenCache();
  let roots = await fetchRootPage(currentPage.value);
  for (const r of roots) await loadSubtree(r, 1, 8, opts?.force);
  allRoots.value = roots;
  rebuildIndex();

  await nextTick();

  if (!opts?.focusId) {
    const first = allRoots.value[0];
    if (first) {
      currentNode.value = first;
      treeRef.value?.setCurrentKey(first.id);
      const node = treeRef.value?.getNode(first.id);
      breadcrumb.value = node ? buildPathForBreadcrumb(node) : [first];
    } else {
      currentNode.value = null;
      breadcrumb.value = [];
    }
  } else {
    await focusNodeById(opts.focusId);
  }

  treeRef.value?.setCheckedKeys(selectedIds.value, false);
  emit("loaded", { roots: allRoots.value, total: rootTotalRef.value });
}

async function reloadSearch(opts = { focusId: null, searchopt: null, force: false }) {
  if (opts?.force) resetChildrenCache();
  const roots = await selfSearchData(currentPage.value);
  for (const r of roots) await loadSubtree(r, 1, 8, opts?.force);
  allRoots.value = roots;
  rebuildIndex();
  await nextTick();

  const name = (queryParams.value.name || "").trim();
  const targetId = (opts && opts.focusId) || (name ? findBestMatchIdByName(name) : null) || getLastLeafId();

  if (targetId) {
    await focusNodeById(targetId);
  } else {
    currentNode.value = null;
    breadcrumb.value = [];
  }
  treeRef.value && treeRef.value.setCheckedKeys(selectedIds.value, false);
  emit("loaded", { roots: allRoots.value, total: rootTotalRef.value });
}

/* ======= 详情/删除示例（保留常用动作，父级也可用插槽接管） ======= */
const detailVisible = ref(false);
const detailRow = ref(null);
function openDetail(row) {
  detailRow.value = row;
  detailVisible.value = true;
}
async function removeNode(row) {
  let parent_id = row.pid;
  try {
    await ElMessageBox.confirm(`确定删除【${row.name}】吗？`, "提示", { type: "warning" });
  } catch { return; }
  try {
    await delSbtz(row.id);
    ElMessage.success("删除成功");
    await reload({ focusId: parent_id });
  } catch (error) {
    ElMessage.error("删除失败：" + (error?.message || "未知错误"));
  }
}

/* ======= 暴露可供父组件调用的方法 ======= */
defineExpose({
  reload,
  reloadSearch,
  focusNodeById,
});

/* ======= 初始化 ======= */
onMounted(() => {
  // 初次加载
  reload();
});

// 工具：时间格式化（与项目保持一致）
const { proxy } = getCurrentInstance();
const parseTime = proxy?.parseTime || ((t) => t);
</script>

<style scoped>
.mb12{margin-bottom:12px;}
.flex{display:flex;}
.items-center{align-items:center;}
.justify-between{justify-content:space-between;}
.gap-8{gap:8px;}
.ml4{margin-left:4px;}

.main-row {
  height: v-bind(height);
  overflow-y: hidden;   /* 只隐藏Y轴 */
  overflow-x: visible;  /* 放开X轴，滚动条就能显示出来 */
}
.inputWidth{
  width: 200px;
}

.main-row>.el-col{height:v-bind(height); display:flex;}
.left-card{width:100%; height:100%;}
.left-card :deep(.el-card__body){height:100%; padding:0;}
.left-body{display:grid; grid-template-rows:auto 1fr auto; height:100%;}
.left-middle{min-height:0; overflow:auto; padding:12px;}

.main-row > .el-col:last-child,
.main-row > .el-col:last-child .el-card,
.main-row > .el-col:last-child :deep(.el-card__body) {
  overflow: visible;
}


.asset-tree :deep(.el-tree-node__content){width:100% !important; height:30px;}
.asset-tree :deep(.node-line){
  display:grid; grid-template-columns:1fr auto; align-items:center; gap:8px; width:100%;
  cursor:pointer; white-space:nowrap;
}
.asset-tree :deep(.node-left){display:inline-flex; align-items:center; gap:6px; min-width:0;}
.asset-tree :deep(.node-left .name-colored){overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:var(--lvl-color); font-weight:400;}
.asset-tree :deep(.node-right){display:inline-flex; align-items:center; gap:6px;}

.root-tools{margin-left:8px; display:inline-flex; align-items:center;}
.root-arrow{
  display:inline-flex; align-items:center; justify-content:center; width:18px; height:18px; border-radius:50%;
  font-size:12px; line-height:1; cursor:pointer; user-select:none; border:1px solid var(--el-border-color); background:var(--el-fill-color-blank);
}
.root-arrow[data-state="open"]{color:var(--el-color-warning);}
.root-arrow[data-state="close"]{color:var(--el-color-success);}

:deep(.el-table){font-size:14px; border-radius:8px; overflow:hidden; border:1px solid #ebeef5;}
:deep(.el-table th.el-table__cell){background-color:#f9fafb; font-weight:600; color:#303133; height:48px; border-bottom:2px solid #ebeef5;}
:deep(.el-table .el-table__cell){height:48px; padding:8px 12px;}
:deep(.el-table--striped .el-table__body tr.el-table__row--striped td){background:#fcfcfd;}
:deep(.el-table__body tr:hover>td){background-color:#f0f9ff !important;}
:deep(.el-table__body tr.row-current>td){background:var(--el-color-success-light-8)!important; border-color:transparent!important;}
:deep(.el-table__body tr.row-current+tr>td){border-top-color:transparent!important;}
:deep(.el-table__fixed::before), :deep(.el-table__fixed-right::before){background-color:transparent!important; box-shadow:none!important;}

:deep(.name-code-cell){display:flex; align-items:center; justify-content:space-between; width:100%; gap:8px;}
:deep(.cell-left){display:inline-flex; align-items:center; gap:6px; min-width:0;}
:deep(.name-text){font-weight:600; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
:deep(.cell-right){display:inline-flex; align-items:center; gap:6px; flex-shrink:0;}
.tag-soft{
  --tag-fg:#606266; --tag-bg:#f5f7fa; --tag-bd:#e4e7ed;
  color:var(--tag-fg)!important; background:var(--tag-bg)!important; border-color:var(--tag-bd)!important;
  border-radius: var(--el-border-radius-base)!important;
}
.desc-box :deep(.el-descriptions__cell.is-bordered-label){width:200px; white-space:nowrap;}
.desc-box :deep(.el-descriptions__content){white-space:normal; word-break:break-all;}
.one-line{display:inline-block; max-width:100%; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
</style>
